package org.desktop.web.client.user.form;

import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.desktop.web.client.customer.form.field.CustomerFormProperty;
import org.desktop.web.client.customer.proxy.RpcCustomerProxy;
import org.desktop.web.client.email.grid.property.EmailProperties;
import org.desktop.web.client.emails.EmailService;
import org.desktop.web.client.emails.EmailServiceAsync;
import org.desktop.web.client.emails.grid.EmailsGrid;
import org.desktop.web.client.field.validator.EmailValidator;
import org.desktop.web.client.gui.models.APagingGrid;
import org.desktop.web.client.gui.models.AbstractGridWindowForm;
import org.desktop.web.client.gui.models.AbstractWindowForm.OperationType;
import org.desktop.web.client.gui.models.PagingComboBox;
import org.desktop.web.client.role.form.field.RoleFormProperty;
import org.desktop.web.client.role.proxy.RpcRolesProxy;
import org.desktop.web.client.user.UserService;
import org.desktop.web.client.user.UserServiceAsync;
import org.desktop.web.client.users.grid.UsersGrid;
import org.desktop.web.client.util.Util;
import org.desktop.web.shared.Response;
import org.desktop.web.shared.domain.entities.CustomersEnabled;
import org.desktop.web.shared.domain.entities.Email;
import org.desktop.web.shared.domain.entities.Role;
import org.desktop.web.shared.domain.entities.User;

import com.google.gwt.core.shared.GWT;
import com.google.gwt.editor.client.Editor;
import com.google.gwt.editor.client.EditorError;
import com.google.gwt.json.client.JSONObject;
import com.google.gwt.json.client.JSONParser;
import com.google.gwt.json.client.JSONValue;
import com.google.gwt.user.client.Cookies;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.IsWidget;
import com.sencha.gxt.core.client.util.Margins;
import com.sencha.gxt.data.shared.ListStore;
import com.sencha.gxt.data.shared.Store;
import com.sencha.gxt.data.shared.Store.Record;
import com.sencha.gxt.data.shared.loader.PagingLoadConfig;
import com.sencha.gxt.widget.core.client.FramedPanel;
import com.sencha.gxt.widget.core.client.box.MessageBox;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer;
import com.sencha.gxt.widget.core.client.container.VerticalLayoutContainer.VerticalLayoutData;
import com.sencha.gxt.widget.core.client.event.SelectEvent;
import com.sencha.gxt.widget.core.client.event.SelectEvent.SelectHandler;
import com.sencha.gxt.widget.core.client.form.FieldLabel;
import com.sencha.gxt.widget.core.client.form.TextField;
import com.sencha.gxt.widget.core.client.form.Validator;
import com.sencha.gxt.widget.core.client.form.error.DefaultEditorError;
import com.sencha.gxt.widget.core.client.info.Info;

public class UserFormWindow extends AbstractGridWindowForm<User> implements IsWidget {

	private FramedPanel mainPanel;

	private TextField login;

	private PagingComboBox<Role, RpcRolesProxy, ListStore<Role>> roles;

	private UserServiceAsync userService = GWT.create(UserService.class);

	// private ComboBox roles;

	private PagingComboBox<CustomersEnabled, RpcCustomerProxy, ListStore<CustomersEnabled>> customers;

	private APagingGrid<org.desktop.web.shared.domain.entities.Email, PagingLoadConfig, EmailProperties> emailsGrid;

	private UsersGrid usersGrid;

	private TextField loginTextField;

	public UserFormWindow(OperationType operationType, UsersGrid toUpdate) {
		super(operationType, toUpdate);
		this.usersGrid = toUpdate;
		setHeadingText("Add user");
	}

	@Override
	public String id() {
		// TODO Auto-generated method stub
		return "USERS_FORM";
	}

	@Override
	public IsWidget createLayout() {
		// TODO Auto-generated method stub
		RoleFormProperty roleProperties = GWT.create(RoleFormProperty.class);
		CustomerFormProperty customersProperties = GWT
				.create(CustomerFormProperty.class);

		/**
		 * Container su cui vengono aggiunti tutti i componenti della finestra.
		 */
		VerticalLayoutContainer vlcMain = new VerticalLayoutContainer();

		VerticalLayoutContainer vlcTop = new VerticalLayoutContainer();
		VerticalLayoutContainer vlcBottom = new VerticalLayoutContainer();

		loginTextField = new TextField();

		loginTextField.setAllowBlank(false);
		vlcTop.add(new FieldLabel(loginTextField, "Login"),
				new VerticalLayoutData(1, -1, new Margins(5, 10, 5, 10)));
		// loginTextField.setValidateOnBlur(true);
		loginTextField.addValidator(new Validator<String>() {

			@Override
			public List<EditorError> validate(Editor<String> editor,
					String value) {
				List<EditorError> errors = new ArrayList<EditorError>();
				if (value != null && value.length() < 5) {
					errors.add(new DefaultEditorError(editor,
							"La login deve essere di almeno 5 caratteri!",
							value));
				}
				return errors;
			}
		});

		ListStore<Role> roleListStore = new ListStore<Role>(roleProperties.id());
		RpcRolesProxy proxy = new RpcRolesProxy();

		this.roles = new PagingComboBox<Role, RpcRolesProxy, ListStore<Role>>(
				proxy, roleListStore, roleProperties.descRole());

		vlcTop.add(new FieldLabel(this.roles.asWidget(), "Ruolo"),
				new VerticalLayoutData(1, -1, new Margins(5, 10, 5, 10)));

		this.roles.setAllowBlank(false);
		this.roles.setName("ruolo");
		// this.roles.setLoader(loader);
		this.roles.setTypeAhead(true);
		this.roles.setEmptyText("Select a role...");
		this.roles.setPageSize(20);
		this.roles.setMinChars(3);

		RpcCustomerProxy customerProxy = new RpcCustomerProxy();
		ListStore<CustomersEnabled> customersStore = new ListStore<CustomersEnabled>(
				customersProperties.id());
		this.customers = new PagingComboBox<CustomersEnabled, RpcCustomerProxy, ListStore<CustomersEnabled>>(
				customerProxy, customersStore,
				customersProperties.customerDesc());

		vlcTop.add(new FieldLabel(this.customers.asWidget(), "Clienti"),
				new VerticalLayoutData(1, -1, new Margins(5, 10, 5, 10)));

		this.customers.setAllowBlank(false);
		this.customers.setName("customer");
		// this.roles.setLoader(loader);
		this.customers.setTypeAhead(true);
		this.customers.setEmptyText("Select a customer...");
		this.customers.setPageSize(20);
		this.customers.setMinChars(3);
		Window.alert("" + getOperationType());
		switch (getOperationType()) {
		case UPDATE:
			loginTextField.setEnabled(false);
			loginTextField.setText(usersGrid.getGrid().getSelectionModel()
					.getSelectedItem().getLogin());
			this.roles.getCombo().getLoader().load();
			this.roles.getCombo().setValue(
					usersGrid.getGrid().getSelectionModel().getSelectedItem()
							.getRole());
			this.customers.getCombo().getLoader().load();
			this.customers.getCombo().setValue(
					usersGrid.getGrid().getSelectionModel().getSelectedItem()
							.getCustomersEnabled());
			Cookies.setCookie(
					"idUser",
					Integer.toString(usersGrid.getGrid().getSelectionModel()
							.getSelectedItem().getId()));
			break;
		}

		this.emailsGrid = new EmailsGrid(20);

		vlcBottom.add(this.emailsGrid.asWidget(), new VerticalLayoutData(1, 1,
				new Margins(5, 10, 5, 10)));

		vlcMain.add(vlcTop, new VerticalLayoutData(1, 0.5));
		vlcMain.add(vlcBottom, new VerticalLayoutData(1, 0.5));
		//
		// ButtonBar buttonBar = new ButtonBar();
		// buttonBar.setPack(BoxLayoutPack.CENTER);
		// TextButton saveButton = new TextButton();
		// saveButton.setIcon(Resources.IMAGES.save());
		// saveButton.addSelectHandler();
		// buttonBar.add(saveButton);
		// vlcMain.add(buttonBar, new VerticalLayoutData(1, -1));

		// this.mainPanel.add(vlcMain);
		// this.add(mainPanel);

		this.setWidth(500);
		this.setHeight(458);

		return vlcMain;
	}

	@Override
	public SelectHandler getInsertHandler() {
		return new SelectHandler() {

			@Override
			public void onSelect(SelectEvent event) {
				EmailsGrid eg = (EmailsGrid) emailsGrid;
				if (validForm(eg) && validGrid(eg)) {
					// verifico i record modificati
					User user = bind(eg, new User());
					Map<String, String> headers = new HashMap<String, String>();
					headers.put("token", Cookies.getCookie("token"));
					headers.put("Referer", Window.Location.getHref());
					userService.saveUser(headers, user,
							new AsyncCallback<Response>() {

								@Override
								public void onFailure(Throwable th) {

									Window.alert(th.getMessage());
								}

								@Override
								public void onSuccess(Response result) {
									Window.alert("result"
											+ result.getJsonMetadata());
									Util.showWaitCursor();
									if (!result.isSuccess()) {
										JSONValue values = JSONParser
												.parseStrict(result
														.getJsonMetadata());
										JSONObject obj = values.isObject();
										String error = obj.get("ERROR")
												.isString().stringValue();
										Util.showDefaultCursor();
										Util.showAlert("Error", error,
												MessageBox.ICONS.error());
										return;
									}
									Util.showDefaultCursor();
									usersGrid.load();
									hide();
								}
							});
					// TODO il delete va gestito a parte!!! ed è gestibile solo
					// in
					// caso di modifica utente

				}
			}

		};
	}

	@Override
	public SelectHandler getUpdateHandler() {
		// TODO Auto-generated method stub
		return new SelectHandler() {

			@Override
			public void onSelect(SelectEvent event) {
				final EmailsGrid eg = (EmailsGrid) emailsGrid;
				if (validForm(eg) && validGrid(eg)) {
					User user = bind(eg, usersGrid.getGrid()
							.getSelectionModel().getSelectedItem());
					List<Email> emails = new ArrayList<Email>();
					for (Iterator iterator = eg.getGrid().getStore().getAll()
							.iterator(); iterator.hasNext();) {
						Email email = (Email) iterator.next();
						Info.display("Id Email: ", email.getIdEmail()+"");
						email.setIdUsers(user);
						emails.add(email);
						
					}
					user.setEmails(emails);
					Window.alert("Id User: " + user.getId());
					Map<String, String> headers = new HashMap<String, String>();
					headers.put("token", Cookies.getCookie("token"));
					userService.update(Cookies.getCookie("token"), user,
							new AsyncCallback<Boolean>() {

								@Override
								public void onFailure(Throwable t) {
									Util.showAlert("Error", t.getMessage(), MessageBox.ICONS.error());
								}

								@Override
								public void onSuccess(Boolean result) {
									if(result ){
										Util.showAlert("Success", "I dati dell'utente sono stati aggiornati!", MessageBox.ICONS.info());
										if(eg.getDeletedMap()!=null && eg.getDeletedMap().size()>0){
											EmailServiceAsync emailService = GWT.create(EmailService.class);
											Collection<Email> cEmail = eg.getDeletedMap().values();
											for (Iterator<Email> iterator = cEmail
													.iterator(); iterator
													.hasNext();) {
												Email email = iterator
														.next();
												emailService.delete(Util.getHeaders(),email, new AsyncCallback<Boolean>() {

													@Override
													public void onFailure(
															Throwable arg0) {
														//ingoro il risultato. male che vada non viene cancellato
														//il servizio remoto restituisce un 404 se non trova l'email in archivio
														return;
													}

													@Override
													public void onSuccess(
															Boolean result) {
														//anche qui non faccio nulla.
														return;
													}
												});
												
											}
										}
										hide();
										return;
									}else{
										Util.showAlert("Error", "Si è verificato un errore durante l'aggiornamento!\nContattare l'amministratore del servizio", MessageBox.ICONS.error());
									}
								}
							});
					

				}
			}
		};
	}

	private boolean validGrid(EmailsGrid eg) {
		eg.getGrid().getStore().commitChanges();
		Collection<Store<Email>.Record> records = eg.getGrid().getStore()
				.getModifiedRecords();
		for (Iterator iterator = records.iterator(); iterator.hasNext();) {
			Record record = (Record) iterator.next();
			Email email = (Email) record.getModel();
			if (!EmailValidator.isValid(email.getEmail())) {
				Util.showAlert("Error",
						"Indirizzo email non valido: " + email.getEmail(),
						MessageBox.ICONS.error());
				return false;
			}
		}
		// verifico anche quelli non modificati. Questo perchè se viene
		// creata una nuova riga alla griglia e non viene fatta nessuna
		// modifica, lo store mantiene la mail di default
		List<Email> emails = eg.getGrid().getStore().getAll();
		for (Iterator iterator = emails.iterator(); iterator.hasNext();) {
			Email email = (Email) iterator.next();
			if (!EmailValidator.isValid(email.getEmail())) {
				Util.showAlert("Error",
						"Indirizzo email non valido: " + email.getEmail(),
						MessageBox.ICONS.error());
				return false;
			}

		}
		return true;
	}

	private boolean validForm(EmailsGrid eg) {
		loginTextField.isValid();
		roles.getCombo().isValid();
		customers.getCombo().isValid();

		if (loginTextField.getErrors().size() > 0
				|| roles.getCombo().getErrors().size() > 0
				|| customers.getCombo().getErrors().size() > 0) {
			Util.showAlert("Error", "I dati inseriti contengono errori",
					MessageBox.ICONS.error());
			return false;
		}
		if (eg.getStore().size() == 0) {
			Util.showAlert("Error", "Inserire almeno un indirizzo email!",
					MessageBox.ICONS.error());
			return false;
		}
		return true;
	}

	private User bind(EmailsGrid eg, User user) {
		Collection<Email> update = eg.getUpdatedMap().values();
		Collection<Email> add = eg.getAddedMap().values();
		Collection<Email> deleted = eg.getDeletedMap().values();

		user.setLogin(loginTextField.getText());
		user.setFirstLogin(true);
		user.setLocked(false);
		user.setRole(roles.getCombo().getCurrentValue());
		user.setCustomersEnabled(customers.getCombo().getCurrentValue());
		List<Email> emails4User = new ArrayList<Email>();
		for (Iterator<Email> iterator = update.iterator(); iterator.hasNext();) {
			Email temp = iterator.next();
			if (OperationType.INSERT.equals(getOperationType())) {
				temp.setIdEmail(null);
			}
			emails4User.add(temp);

		}
		for (Iterator<Email> iterator = add.iterator(); iterator.hasNext();) {
			Email temp = iterator.next();
			temp.setIdEmail(null);
			emails4User.add(temp);

		}
		user.setEmails(emails4User);
		return user;
	}

}

/**
 * case MODIFY: userService.update(Cookies.getCookie("token"), user, new
 * AsyncCallback<Boolean>() {
 * 
 * @Override public void onFailure(Throwable arg0) { // TODO Auto-generated
 *           method stub
 * 
 *           }
 * @Override public void onSuccess(Boolean result) { if(result){
 *           usersGrid.load(); hide(); }
 * 
 *           } }); break;
 */
